﻿using System;
using System.ComponentModel;
using System.Runtime.ConstrainedExecution;
using System.Runtime.InteropServices;
using System.Security;
using System.Text;

namespace Microsoft.Win32
{
	internal static partial class NativeMethods
	{
		private const string KERNEL32 = "Kernel32.dll";

		[UnmanagedFunctionPointer(CallingConvention.Winapi, SetLastError = true, CharSet = CharSet.Unicode)]
		[SuppressUnmanagedCodeSecurity]
		public delegate bool ENUMRESNAMEPROC(IntPtr hModule, IntPtr lpszType, IntPtr lpszName, IntPtr lParam);

		[Flags]
		public enum FormatMessageFlags
		{
			AllocateBuffer = 0x100,
			ArgumentArray = 0x2000,
			FromHModule = 0x800,
			FromString = 0x400,
			FromSystem = 0x1000,
			IgnoreInserts = 0x200,
			MaxWidthMask = 0xFF
		}

		[System.Flags]
		public enum LoadLibraryFlags : uint
		{
			DONT_RESOLVE_DLL_REFERENCES         = 0x00000001,
			LOAD_LIBRARY_AS_DATAFILE            = 0x00000002,
			LOAD_WITH_ALTERED_SEARCH_PATH       = 0x00000008,
			LOAD_IGNORE_CODE_AUTHZ_LEVEL        = 0x00000010,
			LOAD_LIBRARY_AS_IMAGE_RESOURCE      = 0x00000020,
			LOAD_LIBRARY_AS_DATAFILE_EXCLUSIVE  = 0x00000040,
			LOAD_LIBRARY_REQUIRE_SIGNED_TARGET  = 0x00000080,
			LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR    = 0x00000100,
			LOAD_LIBRARY_SEARCH_APPLICATION_DIR = 0x00000200,
			LOAD_LIBRARY_SEARCH_USER_DIRS       = 0x00000400,
			LOAD_LIBRARY_SEARCH_SYSTEM32        = 0x00000800,
			LOAD_LIBRARY_SEARCH_DEFAULT_DIRS    = 0x00001000,
		}

		[ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success), DllImport(KERNEL32, SetLastError = true)]
		[return: MarshalAs(UnmanagedType.Bool)]
		public static extern bool CloseHandle(IntPtr handle);

		[DllImport(KERNEL32, SetLastError = true, CharSet = CharSet.Unicode)]
		[SuppressUnmanagedCodeSecurity]
		[return: MarshalAs(UnmanagedType.Bool)]
		public static extern bool EnumResourceNames(IntPtr hModule, IntPtr lpszType, ENUMRESNAMEPROC lpEnumFunc, IntPtr lParam);

		[DllImport(KERNEL32, SetLastError = true, CharSet = CharSet.Unicode)]
		[SuppressUnmanagedCodeSecurity]
		public static extern IntPtr FindResource(IntPtr hModule, IntPtr lpName, IntPtr lpType);

		[DllImport(KERNEL32, CharSet = CharSet.Auto, SetLastError = true)]
		public static extern uint FormatMessage(FormatMessageFlags dwFlags, IntPtr lpSource, uint dwMessageId, uint dwLanguageId, [Out] System.Text.StringBuilder lpBuffer, uint nSize, string[] Arguments);

		[DllImport(KERNEL32, SetLastError = true)]
		[return: MarshalAs(UnmanagedType.Bool)]
		public static extern bool FreeLibrary(IntPtr lib);

		[DllImport(KERNEL32, SetLastError = true, CharSet = CharSet.Auto)]
		public static extern uint GetCompressedFileSize(string lpFileName, out uint lpFileSizeHigh);

		[DllImport(KERNEL32, CharSet = CharSet.Auto, SetLastError = true)]
		public static extern IntPtr GetCurrentProcess();

		[DllImport(KERNEL32, CharSet = CharSet.Auto, SetLastError = true)]
		public static extern IntPtr GetCurrentThread();

		[DllImport(KERNEL32, SetLastError = true, CharSet = CharSet.Auto)]
		[return: MarshalAs(UnmanagedType.Bool)]
		public static extern bool GetDiskFreeSpace(string lpRootPathName, out uint lpSectorsPerCluster, out uint lpBytesPerSector, out uint lpNumberOfFreeClusters, out uint lpTotalNumberOfClusters);

		[SecurityCritical, SuppressUnmanagedCodeSecurity, DllImport(KERNEL32, EntryPoint = "GetModuleFileName", CharSet = CharSet.Unicode, SetLastError = true)]
		public static extern int GetModuleFileName(HandleRef hModule, StringBuilder buffer, int length);

		[SecurityCritical]
		public static string GetModuleFileName(HandleRef hModule)
		{
			int num1;
			StringBuilder buffer = new StringBuilder(260);
			Label_000B:
			num1 = GetModuleFileName(hModule, buffer, buffer.Capacity);
			if (num1 == 0)
				throw new System.ComponentModel.Win32Exception();
			if (num1 == buffer.Capacity)
			{
				buffer.EnsureCapacity(buffer.Capacity * 2);
				goto Label_000B;
			}
			return buffer.ToString();
		}

		/// <summary>
		/// The GlobalLock function locks a global memory object and returns a pointer to the first byte of the object's memory block.
		/// GlobalLock function increments the lock count by one.
		/// Needed for the clipboard functions when getting the data from IDataObject
		/// </summary>
		/// <param name="hMem"></param>
		/// <returns></returns>
		[DllImport(KERNEL32, SetLastError = true)]
		public static extern IntPtr GlobalLock(IntPtr hMem);

		/// <summary>
		/// The GlobalUnlock function decrements the lock count associated with a memory object.
		/// </summary>
		/// <param name="hMem"></param>
		/// <returns></returns>
		[DllImport(KERNEL32, SetLastError = true)]
		[return: MarshalAs(UnmanagedType.Bool)]
		public static extern bool GlobalUnlock(IntPtr hMem);

		[DllImport(KERNEL32, CharSet = CharSet.Auto, SetLastError = true, BestFitMapping = false, ThrowOnUnmappableChar = true)]
		public static extern IntPtr LoadLibrary([In, MarshalAs(UnmanagedType.LPTStr)] string filename);

		[DllImport(KERNEL32, CharSet = CharSet.Auto, SetLastError = true, BestFitMapping = false, ThrowOnUnmappableChar = true)]
		[SuppressUnmanagedCodeSecurity]
		public static extern IntPtr LoadLibraryEx([In, MarshalAs(UnmanagedType.LPTStr)] string lpFileName, IntPtr hFile, LoadLibraryFlags dwFlags);

		[DllImport(KERNEL32, SetLastError = true)]
		[SuppressUnmanagedCodeSecurity]
		public static extern IntPtr LoadResource(IntPtr hModule, IntPtr hResInfo);

		[DllImport(KERNEL32, SetLastError = true)]
		[SuppressUnmanagedCodeSecurity]
		public static extern IntPtr LockResource(IntPtr hResData);

		[DllImport(KERNEL32, SetLastError = true, CharSet = CharSet.Unicode)]
		[SuppressUnmanagedCodeSecurity]
		public static extern int QueryDosDevice(string lpDeviceName, StringBuilder lpTargetPath, int ucchMax);

		[DllImport(KERNEL32, SetLastError = true)]
		[SuppressUnmanagedCodeSecurity]
		public static extern uint SizeofResource(IntPtr hModule, IntPtr hResInfo);

		public partial class SafeLibraryHandle : SafeHandles.SafeHandleZeroOrMinusOneIsInvalid
		{
			public SafeLibraryHandle(string fileName, LoadLibraryFlags flags = 0) : base(true)
			{
				var hLib = LoadLibraryEx(fileName, IntPtr.Zero, flags);
				if (hLib == IntPtr.Zero)
					throw new Win32Exception();
				SetHandle(hLib);
			}

			internal SafeLibraryHandle(IntPtr handle, bool own = true) : base(own) { SetHandle(handle); }

			protected override bool ReleaseHandle() => FreeLibrary(handle);
		}

		public partial class SafeTokenHandle : SafeHandles.SafeHandleZeroOrMinusOneIsInvalid
		{
			internal SafeTokenHandle(IntPtr handle, bool own = true) : base(own) { SetHandle(handle); }

			private SafeTokenHandle() : base(true) { }

			protected override bool ReleaseHandle() => CloseHandle(handle);
		}
	}
}